package mathive.client.panels;

import java.util.Vector;

import mathive.client.ClientSudokuData;
import mathive.client.GameParent;
import mathive.client.PanelHandler;
import mathive.client.SudokuService;
import mathive.client.SudokuServiceAsync;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

public class SudokuPanel extends GameParent{

	private Grid sudokuGrid = new Grid(9, 9);
	private Button sudokuDoneButton = new Button("Check Sudoku");
	private Button cancelButton = new Button("Cancel");
	private SudokuServiceAsync sudokuSvc = GWT.create(SudokuService.class);
	private String sudoku;
	private HTML title;
	private HTML requestHtml;
	private VerticalPanel mainPanel = new VerticalPanel();
	private Label idLabel = new Label("ID");
	private Label diffLabel = new Label("Difficulty");
	private TextBox idBox = new TextBox();
	private ListBox diffBox = new ListBox();
	Button getSdkBtn = new Button("Play!");

	public SudokuPanel(){

		title = new HTML("<h1>Sudoku</h1>");
		requestHtml = new HTML("<p>Please choose which sudoku you want to play.<br>" +
		"Either choose an ID or a difficulty setting to pick a random puzzle.</p>");
		sudokuGrid.setCellPadding(0);
		sudokuGrid.setCellSpacing(0);
		sudokuGrid.setStyleName("sudokuGrid");
		sudokuDoneButton.setEnabled(true);
		sudokuDoneButton.addStyleName("sudokuDoneButton");

		// Listen for mouse events on the Done button.
		sudokuDoneButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				String result = correctSudoku();
				if (result.equals("correct")){
//					result = "You win!";
					score = 10;
					ResultsPanel resultsPanel = new ResultsPanel();
					resultsPanel.showResult("Sudoku", "1", score);
					mainPanel.clear();
					mainPanel.add(resultsPanel.getPanel());
					return;
				} else if (result.equals("unfinished")){
					result = "You're not finished yet!";
					showResult(result);
					return;
				} else if (result.equals("crazy")){
					result = "You must enter numbers everywhere!";
					showResult(result);
					return;
				}if (result.equals("wrong")){
//					result = "You lose!";
					score = 0;
					ResultsPanel resultsPanel = new ResultsPanel();
					resultsPanel.showResult("Sudoku", "1", score);
					mainPanel.clear();
					mainPanel.add(resultsPanel.getPanel());
				}
				showResult(result);
			}
		});

		// Listen for mouse events on the cancel button.
		cancelButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				PanelHandler.showGames();
			}
		});
		
		// Listen for mouse events on the get sudoku button.
		getSdkBtn.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				String diffText = diffBox.getItemText(diffBox.getSelectedIndex());
				if(idBox.getText().isEmpty() && diffText.equals("Difficulty"))
					Window.alert("You must choose settings before you can play!");
				if(idBox.getText().matches("[0-9].*")){
					fetchSudoku(Integer.parseInt(idBox.getText()), -1);
				} else if (!diffText.equals("Difficulty")){
					int intDiff = 0;
					if (diffText.equals("Easy"))
						intDiff = ClientSudokuData.EASY;
					if (diffText.equals("Medium"))
						intDiff = ClientSudokuData.MEDIUM;
					if (diffText.equals("Hard"))
						intDiff = ClientSudokuData.HARD;					
					
					fetchSudoku(-1, intDiff);
				}
			}
		});

		diffBox.addItem("Difficulty");
		diffBox.addItem("Easy");
		diffBox.addItem("Medium");
		diffBox.addItem("Hard");
		
		// Property must be set before anything is added
		mainPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);

		mainPanel.setStyleName("sudokuPanel");
		mainPanel.add(title);


	}
	
	private void requestSettings(){
		mainPanel.clear();
		VerticalPanel requestPanel = new VerticalPanel();
		idBox.setText("");
		diffBox.setItemSelected(0, true);
		
		requestPanel.add(title);
		requestPanel.add(requestHtml);
		requestPanel.add(idLabel);
		requestPanel.add(idBox);
		requestPanel.add(diffLabel);
		requestPanel.add(diffBox);
		requestPanel.add(getSdkBtn);
		
		mainPanel.add(requestPanel);
	}

	public Widget getPanel(){
		requestSettings();
		return mainPanel;
	}

	public Widget getPanel(int id){
		fetchSudoku(id, -1);
		return mainPanel;
	}
	
	public Widget getPanel(int id, int diff){
		fetchSudoku(id, diff);
		return mainPanel;
	}

	private String fetchSudoku(int id, int diff){

		// Initialize the service proxy.
		if (sudokuSvc == null) {
			sudokuSvc = GWT.create(SudokuService.class);
		}

		// Set up the callback object.
		AsyncCallback<ClientSudokuData> callback = new AsyncCallback<ClientSudokuData>() {
			public void onFailure(Throwable caught) {
				Window.alert("The server malfunctioned. Please try again.");
			}

			public void onSuccess(ClientSudokuData csd) {
				if(csd == null){
					Window.alert("Unable to find sudoku with the specified ID or difficulty setting. " +
							"Please choose another.");
				} else {
					sudoku = csd.getSudoku();
					if(isValid(sudoku))
						buildSudoku();
					else
						Window.alert("The sudoku you requested was broken. Please choose another ID or difficulty.");
				}
			}
		};

		// Make the call to the fetch sudoku service.
		sudokuSvc.getSudoku(id, diff, callback);
		return null;
	}

	private boolean isValid(String checkSudoku){
		if(sudoku.matches("^[ 1-9]{81}$")){
			return true;
		}
		return false;
	}

	private void buildSudoku(){

		// Add contents to the grid
		int numRows = sudokuGrid.getRowCount();
		int numColumns = sudokuGrid.getColumnCount();

		Character[][] sudoku_array = new Character[9][9];
		int k = 0;
		for(int i = 0; i < numRows; i++){
			for(int j = 0; j < numColumns; j++){
				sudoku_array[i][j] = sudoku.charAt(k);
				k++;
			}
		}

		for (int row = 0; row < numRows; row++) {
			for (int col = 0; col < numColumns; col++) {
				if(sudoku_array[row][col].toString().equals(" ")){
					TextBox normalText = new TextBox();
					normalText.ensureDebugId("cwBasicText-textbox");
					normalText.setStyleName("sudokuField");
					sudokuGrid.setWidget(row, col, normalText);
					sudokuGrid.getCellFormatter().setStyleName(row, col, "sudokuGridCell");
				} else {
					TextBox disabledText = new TextBox();
					disabledText.ensureDebugId("cwBasicText-textbox-disabled");
					disabledText.setText(sudoku_array[row][col].toString());
					disabledText.setEnabled(false);
					disabledText.setStyleName("sudokuField");
					sudokuGrid.setWidget(row, col, disabledText);
					sudokuGrid.getCellFormatter().setStyleName(row, col, "sudokuGridCell");
				}
				sudokuGrid.getColumnFormatter().setStyleName(col, "sudokuGridCol");
			}
			sudokuGrid.getRowFormatter().setStyleName(row, "sudokuGridRow");
		}

		for (int row = 0; row < numRows; row++) {
			sudokuGrid.getWidget(row, 2).addStyleName("sudokuFieldRight");
			sudokuGrid.getWidget(row, 5).addStyleName("sudokuFieldRight");
			for (int col = 0; col < numColumns; col++) {
				sudokuGrid.getWidget(2, col).addStyleName("sudokuFieldBottom");
				sudokuGrid.getWidget(5, col).addStyleName("sudokuFieldBottom");
			}
		}
		
		mainPanel.clear();
		mainPanel.add(title);
		mainPanel.add(sudokuGrid);
		mainPanel.add(sudokuDoneButton);
		score = 0;
	}

	public String correctSudoku(){
		String result = "correct";

		Vector<String> previous = new Vector<String>();

		int numRows = sudokuGrid.getRowCount();
		int numColumns = sudokuGrid.getColumnCount();

		// Check for duplicates in rows
		for (int row = 0; row < numRows; row++) {
			if(result.equals("correct")){
				for(int next = 1; next <numColumns; next++){
					TextBox text = (TextBox)sudokuGrid.getWidget(row, next);
					if(text.getText().equals("")){
						result = "unfinished";
						break;
					} else if(!text.getText().matches("^[1-9]{1}$")){
						result = "crazy";
						break;
					} else if(previous.contains(text.getText())){
						result = "wrong";
						break;
					}
					previous.add(text.getText());
				}
				previous.clear();
			}
		}

		// Check for duplicates in columns
		if(result.equals("correct")){
			for (int col = 0; col < numColumns; col++) {
				if(result.equals("correct")){
					for(int next = 0; next <numColumns; next++){
						TextBox text = (TextBox)sudokuGrid.getWidget(next, col); 
						if(text.getText().equals("")){
							result = "unfinished";
							break;
						} else if(!text.getText().matches("^[1-9]{1}$")){
							result = "crazy";
							break;
						} else if(previous.contains(text.getText())){
							result = "wrong";
							break;
						}
						previous.add(text.getText());
					}
					previous.clear();
				}
			}
		}

		// Check for duplicates in 3x3 boxes
		if(result.equals("correct")){
			int tbtRowCheck = 0;
			while(tbtRowCheck <= 6){
				if(result.equals("correct")){
					int tbtColCheck = 0;
					while(tbtColCheck <= 6){
						for(int rowNum = tbtRowCheck; rowNum < tbtRowCheck + 3; rowNum++){
							for(int colNum = tbtColCheck; colNum < tbtColCheck + 3; colNum++){
								TextBox text = (TextBox)sudokuGrid.getWidget(rowNum, colNum);
								if(text.getText().equals("")){
									result = "unfinished";
									break;
								} else if(!text.getText().matches("^[1-9]{1}$")){
									result = "crazy";
									break;
								} else if(previous.contains(text.getText())){
									result = "wrong";
									break;
								}
								previous.add(text.getText());
							}
						}
						previous.clear();
						tbtColCheck = tbtColCheck + 3;
					}
				} else {
					break;
				}
				tbtRowCheck = tbtRowCheck + 3;
			}
		}

		return result;
	}
	
	private void showResult(String result){
		// Create a dialog box and set the caption text
		final DialogBox dialogBox = new DialogBox();
		dialogBox.ensureDebugId("cwDialogBox");
		dialogBox.setText("Result");
		dialogBox.setStyleName("dialogBox");

		// Create a table to layout the content
		VerticalPanel dialogContents = new VerticalPanel();
		dialogContents.setSpacing(4);
		dialogBox.setWidget(dialogContents);

		// Add some text to the top of the dialog
		TextBox details = new TextBox();
		details.setText(result);
		details.setStyleName("detailsBox");
		dialogContents.add(details);
		dialogContents.setCellHorizontalAlignment(details,
				HasHorizontalAlignment.ALIGN_CENTER);

		// Add a close button at the bottom of the dialog
		Button closeButton = new Button("Close",
				new ClickHandler() {
			public void onClick(ClickEvent event) {
				dialogBox.hide();
			}
		});
		dialogContents.add(closeButton);
		if (LocaleInfo.getCurrentLocale().isRTL()) {
			dialogContents.setCellHorizontalAlignment(closeButton,
					HasHorizontalAlignment.ALIGN_LEFT);

		} else {
			dialogContents.setCellHorizontalAlignment(closeButton,
					HasHorizontalAlignment.ALIGN_RIGHT);
		}
		dialogBox.center();
		dialogBox.show();

	}


}
